package com.smedi.ismedi.personnel.core.port.adapter.persistence.repository.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.smedi.ismedi.authority.starter.util.VerifyHelper;
import com.smedi.ismedi.constructionproject.api.dto.response.resp.ProjectAssignmentResp;
import com.smedi.ismedi.database.starter.annotation.SkipSqlPermission;
import com.smedi.ismedi.personnel.core.application.query.vo.PersonDetailVo;
import com.smedi.ismedi.personnel.core.application.query.vo.ProjectAssignmentVo;
import com.smedi.ismedi.personnel.core.domain.aggregatemodel.Person;
import com.smedi.ismedi.personnel.core.domain.aggregatemodel.Role;
import com.smedi.ismedi.personnel.core.domain.aggregatemodel.RoleAssignment;
import com.smedi.ismedi.personnel.core.domain.aggregatemodel.model.OrganizationAssignment;
import com.smedi.ismedi.personnel.core.domain.aggregatemodel.model.SimplePerson;
import com.smedi.ismedi.personnel.core.domain.dto.RoleAssignmentQuery;
import com.smedi.ismedi.personnel.core.domain.dto.RoleQuery;
import com.smedi.ismedi.personnel.core.domain.repository.PersonRepository;
import com.smedi.ismedi.personnel.core.infrastructure.enumerate.PersonStatusEnum;
import com.smedi.ismedi.personnel.core.port.adapter.convert.AdapterConverter;
import com.smedi.ismedi.personnel.core.port.adapter.persistence.dto.OrganizationAssignmentQuery;
import com.smedi.ismedi.personnel.core.port.adapter.persistence.dto.PersonQuery;
import com.smedi.ismedi.personnel.core.port.adapter.persistence.dto.ProjectAssignmentQuery;
import com.smedi.ismedi.personnel.core.port.adapter.persistence.model.AssignmentEntity;
import com.smedi.ismedi.personnel.core.port.adapter.persistence.model.PersonEntity;
import com.smedi.ismedi.personnel.core.port.adapter.persistence.mybatis.mapper.AssignmentMapper;
import com.smedi.ismedi.personnel.core.port.adapter.persistence.mybatis.mapper.PersonMapper;
import com.smedi.ismedi.personnel.core.port.adapter.service.AuthorityAccessRemoteApiService;
import com.smedi.ismedi.personnel.core.port.adapter.service.ConstructionProjectRemoteApiService;
import com.smedi.ismedi.web.starter.util.DozerUtil;
import io.netty.util.internal.StringUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: xulinjun
 * @Date: 2021-6-8 14:02
 */
@Slf4j
@Repository
@AllArgsConstructor
public class PersonRepositoryImpl extends ServiceImpl<PersonMapper, PersonEntity> implements PersonRepository, IService<PersonEntity> {
    private final DozerUtil dozerUtil;
    private final AdapterConverter adapterConverter;
    private final AssignmentMapper organizationAssignmentMapper;
    private final AuthorityAccessRemoteApiService authorityAccessRemoteApiService;
    private final ConstructionProjectRemoteApiService constructionProjectRemoteApiService;


    @Override
    public Person selectByPersonId(Long personId, Boolean isDelete) {
        LambdaQueryWrapper<PersonEntity> wrapper = new LambdaQueryWrapper<>();
        if (isDelete != null) {
            wrapper.eq(PersonEntity::isDeleted, isDelete);
        }
        wrapper.eq(PersonEntity::getId, personId);
        PersonEntity row = this.getBaseMapper().selectOne(wrapper);
        return dozerUtil.convertor(row, Person.class);
    }

    @Override
    public List<Person> selectByPersonIds(List<Long> personIds, Boolean isDelete) {
        LambdaQueryWrapper<PersonEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(PersonEntity::getId, personIds);
        if (isDelete != null) {
            queryWrapper.eq(PersonEntity::isDeleted, isDelete);
        }
        List<PersonEntity> personEntities = this.getBaseMapper().selectList(queryWrapper);
        return dozerUtil.convertor(personEntities, Person.class);
    }

    @Override
    public int selectIdCardCount(String idCard, Long personId, Boolean isDelete) {
        QueryWrapper<PersonEntity> wrapper = new QueryWrapper();
        wrapper.eq("id_card", idCard.toUpperCase()); // 后台数据库均存储大写身份证号
        if (personId != null) {
            wrapper.ne("id", personId);
        }
        if (isDelete != null) {
            wrapper.eq("is_deleted", isDelete);
        }
        return this.getBaseMapper().selectCount(wrapper);
    }

    @Override
    public int selectMobileCount(String mobile, Long personId, Boolean isDelete) {
        QueryWrapper<PersonEntity> wrapper = new QueryWrapper();
        wrapper.eq("mobile", mobile);
        if (personId != null) {
            wrapper.ne("id", personId);
        }
        if (isDelete != null) {
            wrapper.eq("is_deleted", isDelete);
        }
        return this.getBaseMapper().selectCount(wrapper);
    }

    @Override
    public int selectEmployeeNumCount(String employeeNum, Long personId, Boolean isDelete) {
        QueryWrapper<PersonEntity> wrapper = new QueryWrapper();
        wrapper.eq("employee_num", employeeNum); // 与业务确认，员工编号支持按大小写区分，属于两个工号。
        if (personId != null) {
            wrapper.ne("id", personId);
        }
        if (isDelete != null) {
            wrapper.eq("is_deleted", isDelete);
        }
        return this.getBaseMapper().selectCount(wrapper);
    }

    @Override
    public Person doInsert(Person data) {
        PersonEntity personEntity = dozerUtil.convertor(data, PersonEntity.class);
        this.getBaseMapper().insert(personEntity);
        return dozerUtil.convertor(personEntity, Person.class);
    }

    @Override
    public Person doUpdate(Person data) {
        PersonEntity personEntity = dozerUtil.convertor(data, PersonEntity.class);
        this.getBaseMapper().updateById(personEntity);
        return dozerUtil.convertor(personEntity, Person.class);
    }

    @Override
    public IPage selectPersonPaginationByLastNameAndEmployeeNum(Long offset, Long pageSize, String lastName, String employeeNum, Boolean isDelete) {
        Page<PersonEntity> page = new Page<>(offset, pageSize);
        page.addOrder(OrderItem.asc("id"));

        QueryWrapper<PersonEntity> wrapper = new QueryWrapper();
        wrapper.eq("status", PersonStatusEnum.ENABLE.getValue()); // 启用状态的人员
        if (isDelete != null) {
            wrapper.eq("is_deleted", isDelete);
        }
        if (!StringUtil.isNullOrEmpty(lastName) || !StringUtil.isNullOrEmpty(employeeNum)) {
            wrapper.and(con -> con.like("last_name", lastName).or().like("employee_num", employeeNum));
        }

        IPage pagination = this.getBaseMapper().selectPage(page, wrapper);
        List<Person> list = dozerUtil.convertor(pagination.getRecords(), Person.class);
        pagination.setRecords(list);

        return pagination;
    }

    @Override
    public Person selectByIdCard(String idCard, Boolean isDelete) {
        QueryWrapper<PersonEntity> wrapper = new QueryWrapper();
        wrapper.eq("id_card", idCard.toUpperCase());
        if (isDelete != null) {
            wrapper.eq("is_deleted", isDelete);
        }
        PersonEntity row = this.getBaseMapper().selectOne(wrapper);
        return dozerUtil.convertor(row, Person.class);
    }

    @Override
    public void doDeleteByPersonId(Long personId) {
        LambdaUpdateWrapper<PersonEntity> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(PersonEntity::isDeleted, true);
        wrapper.eq(PersonEntity::getId, personId);
        this.getBaseMapper().update(null, wrapper);
    }

    @Override
    public List<SimplePerson> getSimplePersonList(PersonQuery personQuery) {
        LambdaQueryWrapper<PersonEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();

        boolean hasAboutIdQuery = false;
        Set<Long> personIds = new HashSet<>();
        if (CollectionUtils.isNotEmpty(personQuery.getIds())) {
            personIds.addAll(personQuery.getIds());
            hasAboutIdQuery = true;
        }

        if (VerifyHelper.isAvailable(personQuery.getRoleName())
                || VerifyHelper.isAvailable(personQuery.getRoleNames())) {
            List<Role> roles = authorityAccessRemoteApiService.getRoleList(RoleQuery.builder().name(personQuery.getRoleName()).names(personQuery.getRoleNames()).build());
            if (roles.isEmpty()) {
                return new ArrayList<>();
            }
            List<Long> roleIds = roles.stream().map(Role::getId).collect(Collectors.toList());
            List<RoleAssignment> roleAssignments = authorityAccessRemoteApiService.getRoleAssignmentList(RoleAssignmentQuery.builder().roleIds(roleIds).build());
            if (roleAssignments.isEmpty()) {
                return new ArrayList<>();
            }
            if (VerifyHelper.isNotAvailable(personIds)) {
                personIds.addAll(roleAssignments.stream().map(RoleAssignment::getPersonId).collect(Collectors.toList()));
            }
            if (VerifyHelper.isAvailable(personIds)) {
                personIds.retainAll(roleAssignments.stream().map(RoleAssignment::getPersonId).collect(Collectors.toList()));
            }
            hasAboutIdQuery = true;
        }

        if (VerifyHelper.isAvailable(personQuery.getProjectId())
                || VerifyHelper.isAvailable(personQuery.getProjectJob())
                || VerifyHelper.isAvailable(personQuery.getProjectJobs())
                || VerifyHelper.isAvailable(personQuery.getProjectIds())) {
            List<ProjectAssignmentResp> projectAssignments = constructionProjectRemoteApiService.getProjectAssignmentList(ProjectAssignmentQuery.builder()
                    .projectId(personQuery.getProjectId())
                    .projectIds(personQuery.getProjectIds())
                    .projectJob(personQuery.getProjectJob()).build());
            if (VerifyHelper.isNotAvailable(projectAssignments)) {
                return new ArrayList<>();
            }
            if (VerifyHelper.isNotAvailable(personIds)) {
                personIds.addAll(projectAssignments.stream().map(ProjectAssignmentResp::getPersonId).collect(Collectors.toList()));
            }
            if (VerifyHelper.isAvailable(personIds)) {
                personIds.retainAll(projectAssignments.stream().map(ProjectAssignmentResp::getPersonId).collect(Collectors.toList()));
            }
            hasAboutIdQuery = true;
        }

        if (VerifyHelper.isAvailable(personQuery.getOrganizationId())
                || CollectionUtils.isNotEmpty(personQuery.getOrganizationIds())) {
            LambdaQueryWrapper<AssignmentEntity> assignmentQueryWrapper = new LambdaQueryWrapper<>();
            assignmentQueryWrapper.eq(VerifyHelper.isAvailable(personQuery.getOrganizationId()), AssignmentEntity::getHrOrganizationId, personQuery.getOrganizationId());
            assignmentQueryWrapper.in(VerifyHelper.isAvailable(personQuery.getOrganizationIds()), AssignmentEntity::getHrOrganizationId, personQuery.getOrganizationIds());
            List<AssignmentEntity> assignmentEntities = organizationAssignmentMapper.selectList(assignmentQueryWrapper);
            if (VerifyHelper.isNotAvailable(assignmentEntities)) {
                return new ArrayList<>();
            }
            if (VerifyHelper.isNotAvailable(personIds)) {
                personIds.addAll(assignmentEntities.stream().map(AssignmentEntity::getPersonId).collect(Collectors.toList()));
            }
            if (VerifyHelper.isAvailable(personIds)) {
                personIds.retainAll(assignmentEntities.stream().map(AssignmentEntity::getPersonId).collect(Collectors.toList()));
            }
            hasAboutIdQuery = true;
        }

        if (hasAboutIdQuery && personIds.isEmpty()) {
            return new ArrayList<>();
        }

        if(personQuery.getIsDelete()!=null){
            lambdaQueryWrapper.eq(PersonEntity::isDeleted,personQuery.getIsDelete());
        }

        lambdaQueryWrapper.in(CollectionUtils.isNotEmpty(personIds), PersonEntity::getId, personIds);
        lambdaQueryWrapper.like(StringUtils.isNotBlank(personQuery.getNameLike()), PersonEntity::getLastName, personQuery.getNameLike());
        lambdaQueryWrapper.in(StringUtils.isNotBlank(personQuery.getStatus()), PersonEntity::getStatus, personQuery.getStatus());

        List<PersonEntity> personEntities = this.getBaseMapper().selectList(lambdaQueryWrapper).stream().sorted(Comparator.comparing(PersonEntity::getPersonOrder, Comparator.nullsLast(Integer::compareTo))).collect(Collectors.toList());

        return adapterConverter.personEntityToPeople(personEntities);
    }

    @Override
    public SimplePerson getSimplePersonOne(PersonQuery personQuery) {
        LambdaQueryWrapper<PersonEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();

        lambdaQueryWrapper.eq(VerifyHelper.isAvailable(personQuery.getId()), PersonEntity::getId, personQuery.getId());
        lambdaQueryWrapper.eq(VerifyHelper.isAvailable(personQuery.getMobile()), PersonEntity::getMobile, personQuery.getMobile());
        lambdaQueryWrapper.eq(PersonEntity::isDeleted,false);
        PersonEntity personEntity = this.getBaseMapper().selectOne(lambdaQueryWrapper);

        return adapterConverter.personEntityToPeople(personEntity);
    }

    @Override
    public List<PersonDetailVo> getPersonDetailList(PersonQuery personQuery) {
        List<SimplePerson> simplePersonList = this.getSimplePersonList(personQuery);

        return this.extend(simplePersonList);
    }

    @Override
    @SkipSqlPermission
    public List<OrganizationAssignment> getOrganizationAssignmentList(OrganizationAssignmentQuery organizationAssignmentQuery) {

        LambdaQueryWrapper<AssignmentEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();

        lambdaQueryWrapper.eq(ObjectUtils.isNotEmpty(organizationAssignmentQuery.getHrOrganizationId()), AssignmentEntity::getHrOrganizationId, organizationAssignmentQuery.getHrOrganizationId());
        lambdaQueryWrapper.eq(ObjectUtils.isNotEmpty(organizationAssignmentQuery.getPersonId()), AssignmentEntity::getPersonId, organizationAssignmentQuery.getPersonId());

        lambdaQueryWrapper.in(CollectionUtils.isNotEmpty(organizationAssignmentQuery.getIds()), AssignmentEntity::getId, organizationAssignmentQuery.getIds());
        lambdaQueryWrapper.in(CollectionUtils.isNotEmpty(organizationAssignmentQuery.getHrOrganizationIds()), AssignmentEntity::getHrOrganizationId, organizationAssignmentQuery.getHrOrganizationIds());
        lambdaQueryWrapper.in(CollectionUtils.isNotEmpty(organizationAssignmentQuery.getPersonIds()), AssignmentEntity::getPersonId, organizationAssignmentQuery.getPersonIds());

        List<AssignmentEntity> assignmentEntities = organizationAssignmentMapper.selectList(lambdaQueryWrapper);

        return adapterConverter.assignmentEntityToOrganizationAssignment(assignmentEntities);
    }

    @Override
    @SkipSqlPermission
    public List<Person> selectByMobile(String mobile,Boolean isDelete) {
        QueryWrapper<PersonEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("mobile", mobile);
        if (isDelete != null) {
            wrapper.eq("is_deleted", isDelete);
        }
        List<PersonEntity> personEntityList = this.getBaseMapper().selectList(wrapper);
        return dozerUtil.convertor(personEntityList, Person.class);
    }

    @Override
    public void saveSimplePerson(SimplePerson simplePerson) {
        PersonEntity personEntity = adapterConverter.simplePersonToPersonEntity(simplePerson);
        if (!VerifyHelper.isAvailable(simplePerson.getId())) {
            this.getBaseMapper().insert(personEntity);
        }
        if (VerifyHelper.isAvailable(simplePerson.getId())) {
            this.getBaseMapper().updateById(personEntity);
        }
    }

    private PersonDetailVo extend(SimplePerson person) {
        if (null == person) {
            return null;
        }

        List<SimplePerson> people = new ArrayList<>();
        people.add(person);
        List<PersonDetailVo> personDetailVos = this.extend(people);

        return personDetailVos.get(0);
    }

    private List<PersonDetailVo> extend(List<SimplePerson> people) {
        if (people.isEmpty()) {
            return new ArrayList<>();
        }

        List<PersonDetailVo> personDetailVos = adapterConverter.simplePersonToPersonDetailVo(people);

        Set<Long> ids = new HashSet<>();
        for (SimplePerson person : people) {
            Long id = person.getId();
            if (null != id) {
                ids.add(id);
            }
        }

        Map<Long, List<ProjectAssignmentVo>> personIdProjectAssignmentVosMap = new HashMap<>();
        if (VerifyHelper.isAvailable(ids)) {
            List<ProjectAssignmentResp> projectAssignmentResps = constructionProjectRemoteApiService.getProjectAssignmentList(ProjectAssignmentQuery.builder().personIds(new ArrayList<>(ids)).build());
            List<ProjectAssignmentVo> projectAssignmentVos = adapterConverter.projectAssignmentRespToProjectAssignmentVo(projectAssignmentResps);
            for (ProjectAssignmentVo projectAssignmentVo : projectAssignmentVos) {
                List<ProjectAssignmentVo> projectAssignmentVosByPersonId = personIdProjectAssignmentVosMap.getOrDefault(projectAssignmentVo.getPersonId(), new ArrayList<>());
                projectAssignmentVosByPersonId.add(projectAssignmentVo);
                personIdProjectAssignmentVosMap.put(projectAssignmentVo.getPersonId(), projectAssignmentVosByPersonId);
            }
        }

        Map<Long, OrganizationAssignment> personIdOrganizationAssignmentMap = new HashMap<>();
        if (VerifyHelper.isAvailable(ids)) {
            List<AssignmentEntity> assignmentEntities = organizationAssignmentMapper.selectList(new LambdaQueryWrapper<AssignmentEntity>()
                    .in(true, AssignmentEntity::getPersonId, new ArrayList<>(ids)));
            List<OrganizationAssignment> organizationAssignments = adapterConverter.assignmentEntityToOrganizationAssignment(assignmentEntities);
            for (OrganizationAssignment organizationAssignment : organizationAssignments) {
                personIdOrganizationAssignmentMap.put(organizationAssignment.getPersonId(), organizationAssignment);
            }
        }

        for (PersonDetailVo personDetailVo : personDetailVos) {
            personDetailVo.setProjectAssignments(personIdProjectAssignmentVosMap.getOrDefault(personDetailVo.getId(), new ArrayList<>()));
            personDetailVo.setOrganizationAssignment(personIdOrganizationAssignmentMap.get(personDetailVo.getId()));
        }

        return personDetailVos;
    }
}
